#include "KDebugClient.h"
#include "KDebugDef.h"
#include "KSockAddress.h"
#include "KSocket.h"
#include "KStorage.h"

KDebugAction::KDebugAction(KPageList *pool)
    : KStreamAction(pool), m_state(DebugHeader::Unknown), m_length(sizeof(DebugHeader))
{
}

KDebugClient *KDebugAction::client() const
{
    return static_cast<KDebugClient *>(item());
}

void KDebugAction::abortImp()
{
    KSocket *sock = socket();
    sock->shutdown();
    sock->close();
    client()->abort();
}

void KDebugAction::onError(KDef::SockError type, DWORD error)
{
    abortOnce();
}

void KDebugAction::process(KStorage *storage, bool ok)
{
    if (ok) {
        while (storage->bytesAvailable() >= m_length) {
            switch (m_state) {
            case DebugHeader::Unknown:
                {
                    DebugHeader header;
                    storage->read(&header, m_length);
                    if (header.type == DebugHeader::Attr) {
                        m_length = sizeof(DebugAttr);
                    } else if (header.type == DebugHeader::Info) {
                        m_length = sizeof(DebugInfo);
                    } else {
                        abortOnce();
                        return;
                    }
                    m_state = header.type;
                } break;
            case DebugHeader::Attr:
                {
                    DebugAttr attr;
                    storage->read(&attr, m_length);
                    m_length = sizeof(DebugHeader);
                    m_state = DebugHeader::Unknown;
                    client()->onDebugAttr(attr.backColor, attr.foreColor);
                } break;
            case DebugHeader::Info:
                {
                    DebugInfo info;
                    storage->read(&info, m_length);
                    if (info.length > 0) {
                        m_length = info.length;
                        m_state = info.title ? DebugHeader::Title : DebugHeader::Text;
                    } else {
                        m_length = sizeof(DebugHeader);
                        m_state = DebugHeader::Unknown;
                    }
                } break;
            case DebugHeader::Text:
                {
                    std::string text(m_length, '\0');
                    storage->read(&text[0], m_length);
                    m_length = sizeof(DebugHeader);
                    m_state = DebugHeader::Unknown;
                    client()->onDebugText(text);
                } break;
            case DebugHeader::Title:
                {
                    std::string title(m_length, '\0');
                    storage->read(&title[0], m_length);
                    m_length = sizeof(DebugHeader);
                    m_state = DebugHeader::Unknown;
                    client()->onDebugTitle(title);
                } break;
            default:
                assert(false);
                break;
            }
        }
        receive();
    } else {
        abortOnce();
    }
}

KDebugClient::KDebugClient(KPageList *pool) : m_connection(NULL), m_pool(pool)
{
}

void KDebugClient::abort()
{
    // Double-checked locking
    if (m_connection) {
        KLocker lock(*this);
        if (KDebugAction *connection = m_connection) {
            m_connection = NULL;
            connection->abortOnce();
            onDebugEnd();
        }
    }
}

bool KDebugClient::connect(const KIAddress &address)
{
    return addConnector(KIpAddress(0), address) != NULL;
}

void KDebugClient::onDebugBegin()
{
    KDebugMsg("Remote debug server: connected");
}

void KDebugClient::onDebugText(const std::string &text)
{
    OutputDebugStringA(text.c_str());
}

void KDebugClient::onDebugEnd()
{
    KDebugMsg("Remote debug server: disconnected");
}

void KDebugClient::onStopped()
{
    m_connection = NULL;
}

void KDebugClient::onConnected(KSocket *sock)
{
    KAutoPtr<KDebugAction> action(new KDebugAction(m_pool));
    if (setAction(sock, action)) {
        // Double-checked locking
        if (!m_connection) {
            KLocker lock(*this);
            if (!m_connection) {
                m_connection = action.release();
                onDebugBegin();
                receive(sock);
                return;
            }
        }
        action.release();
    }
}